#include "GfxDriver/WGL/WGLVertexInput.h"

#include "GfxDriver/WGL/WGLGfxDriver.h"
#include "GfxDriver/VertexBuffer.h"
#include "GfxDriver/IndexBuffer.h"

#include <map>

namespace Orca {

	//--------------------------------------------------------------------------
	WGLVertexInput* WGLVertexInput::create(GfxDriver *gfxDriver) {

		return new WGLVertexInput(gfxDriver);
	}

	//--------------------------------------------------------------------------
	WGLVertexInput::WGLVertexInput(GfxDriver *gfxDriver) :
		mGfxDriver(gfxDriver) {

	}

	//--------------------------------------------------------------------------
	WGLVertexInput::~WGLVertexInput() {

	}

	//--------------------------------------------------------------------------
	void WGLVertexInput::bind() {

		// bind vertex streams
		uint32 cnt = (uint32)mVStreamDecl.size();

		for(uint32 i = 0; i < cnt; ++i) {

			VertexStreamDecl &decl = mVStreamDecl[i];

			VertexStreamDecl::TElementIter curr = decl.mElems.begin();
			VertexStreamDecl::TElementIter end = decl.mElems.end();

			while(curr != end) {

				_bindVertexElement(*curr, decl);
				++curr;
			}
		}

		// bind index streams
		// currently we only support 1 index stream
		if( !mIStreamDecl.empty() ) {

			IndexStreamDecl &decl = mIStreamDecl[0];

			GLenum glIndexType = (decl.mIndexType == IDT_16) ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT;

			glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, (GLuint)decl.mIBuf->getAPIHandle() );
			glIndexPointer(glIndexType, 0, (void*)decl.mOffsetInBytes );

			static_cast<WGLGfxDriver*>(mGfxDriver)->setCurrentGLIndexType(glIndexType);
		}
	}

	//--------------------------------------------------------------------------
	void WGLVertexInput::_bindVertexElement(
		  const VertexElement &elem
		, VertexStreamDecl &decl) {

		glBindBuffer( GL_ARRAY_BUFFER, (GLuint)decl.mVBuf->getAPIHandle() );

		GLint dataSize		= _getVertexElementDataSize(elem);
		GLenum dataType		= _getVertexElementDataType(elem);
		GLsizei dataStride	= (GLsizei)decl.mStride;
		uint32 dataOffset	= decl.mOffsetInBytes + elem.offset;

		switch(elem.usage) {

			case VEU_POS:
				{
					glEnableClientState(GL_VERTEX_ARRAY);
					glVertexPointer(dataSize, dataType, dataStride, (void*)dataOffset);
				} break;

			case VEU_NORMAL:
				{
					glEnableClientState(GL_NORMAL_ARRAY);
					glNormalPointer(dataType, dataStride, (void*)dataOffset);
				} break;

			case VEU_DIFFUSE:
				{
					glEnableClientState(GL_COLOR_ARRAY);
					glColorPointer(dataSize, dataType, dataStride, (void*)dataOffset);
				} break;

			case VEU_TEXCOORD:
				{
					glClientActiveTexture( (GLenum)(GL_TEXTURE0 + elem.index) );
					glEnableClientState(GL_TEXTURE_COORD_ARRAY);
					glTexCoordPointer(dataSize, dataType, dataStride, (void*)dataOffset);

				} break;

			default:
				break;
		}
	}

	//--------------------------------------------------------------------------
	GLint WGLVertexInput::_getVertexElementDataSize(const VertexElement &elem) {

		if(elem.format == VEDT_RGBA)
			return 4;

		return GLsizei( (elem.format & 0xffff0000) >> 16 );
	}

	//--------------------------------------------------------------------------
	GLenum WGLVertexInput::_getVertexElementDataType(const VertexElement &elem) {

		static std::map<VertexElementDataType, GLenum> vertDataTypeMapping;

		if( vertDataTypeMapping.empty() ) {

			vertDataTypeMapping[VEDT_FLOAT]		= GL_FLOAT;
			vertDataTypeMapping[VEDT_BYTE]		= GL_BYTE;
			vertDataTypeMapping[VEDT_UBYTE]		= GL_UNSIGNED_BYTE;
			vertDataTypeMapping[VEDT_SHORT]		= GL_SHORT;
			vertDataTypeMapping[VEDT_USHORT]	= GL_UNSIGNED_SHORT;
			vertDataTypeMapping[VEDT_INT]		= GL_INT;
			vertDataTypeMapping[VEDT_UINT]		= GL_UNSIGNED_INT;
			vertDataTypeMapping[VEDT_RGBA]		= GL_UNSIGNED_BYTE;
		}

		return vertDataTypeMapping[ VertexElementDataType(elem.format & 0x0000ffff) ];
	}

	//--------------------------------------------------------------------------
}